# ------------------------------------------------------------------------
# -*- coding: utf-8 -*-
# @Time    : 2022/10/2 17:23
# @Author  : Leii
# @File    : Reachability_diagram.py
# @Code instructions: 
# ------------------------------------------------------------------------
import numpy as np
from numpy import random
import networkx as nx
import matplotlib.pyplot as plt


def get_index(item, lst):
    for index, a in enumerate(lst):
        if a == item:
            return int(index)
        else:
            continue


def is_in_it(item, lst):
    for b in lst:
        if b == item:
            return False
            break
    return True


class ReachabilityDiagram():
    def __init__(self, m0, pre, post):
        self.pre = pre  # 前置关联矩阵
        self.post = post  # 后置关联矩阵
        self.m = None
        self.m_ = None  # 新的状态
        self.V = []  # 结点的集合
        self.V_new = []  # 新结点的集合
        self.T = []
        self.t = None  # 单个激发态
        self.V.append(m0.tolist())
        self.V_new.append(m0.tolist())
        self.E = []

    def update_V_new(self):
        if self.V_new:
            x = random.randint(len(self.V_new))
            self.m = np.array(self.V_new[x])  # 获取可达结点中的随机结点
            del self.V_new[x]  # 把m从V_new中删除

    def is_empty_V_new(self):
        var = False if self.V_new else True
        return var

    def is_Transition(self):  # 当前状态下可以激发的变迁
        self.update_V_new()
        for mark in range(0, len(self.pre.T)):  # range(self.pre.shape[1])
            Transition = self.m - self.pre.T[mark]  # self.pre[:, i]
            if np.all(Transition >= 0):
                self.T.append(mark)
        if not self.T:
            return False
        else:
            return True

    def update_V(self):
        m_ = self.m_.tolist()
        m = self.m.tolist()
        if is_in_it(m_, self.V):
            self.V.append(m_)
            self.V_new.append(m_)
        edge = [get_index(m, self.V), get_index(m_, self.V), self.t]  # edge是一个三元组
        if is_in_it(edge, self.E):
            self.E.append(edge)

    def update_m(self):  # 更新当前的标识
        x_fired = []  # 存放激发过后的变迁
        if self.is_Transition():
            while len(self.T):
                x = random.randint(len(self.T))
                self.t = self.T[x]  # 得到激发状态(二值化数值)
                self.m_ = self.post.T[self.T[x]] - self.pre.T[self.T[x]] + self.m  # 步骤7
                if np.all(self.m_ >= 0):
                    self.update_V()  # 更新根节点
                if x not in x_fired:  # 如果激发后的变迁不在已激发的列表中就添加进去
                    x_fired.append(x)
                if len(x_fired) == len(self.T):
                    self.T = []  # 将其从T中删除


if __name__ == '__main__':
    m0 = np.array([1, 0, 1, 0, 0, 2, 0])

    pre = np.array([[1, 0, 0, 0, 0, 0],  # p1
                    [0, 1, 0, 0, 0, 0],  # p2
                    [0, 0, 1, 0, 0, 1],  # p3
                    [0, 0, 0, 1, 0, 0],  # p4
                    [0, 0, 0, 0, 1, 0],  # p5
                    [0, 0, 0, 0, 1, 0],  # p6
                    [0, 0, 0, 0, 0, 1]])  # p7

    post = np.array([[0, 0, 0, 0, 0, 1],  # p1
                     [1, 0, 0, 0, 0, 0],  # p2
                     [0, 1, 0, 0, 0, 0],  # p3
                     [0, 1, 0, 0, 0, 0],  # p4
                     [0, 0, 1, 0, 0, 0],  # p5
                     [0, 0, 0, 1, 0, 0],  # p6
                     [0, 0, 0, 0, 1, 0]])  # p7

    C = post - pre
    print(C)
    W = []  # 权重参数
    my_NET = ReachabilityDiagram(m0, pre, post)
    # for i in range(10):
    while True:
        my_NET.update_m()
        if my_NET.is_empty_V_new():
            print("输出可达图")
            break
        else:
            continue
    # 可达图
    print("V:", len(my_NET.V))
    for i in my_NET.V:
        print(i)

    print("E:")
    for j in my_NET.E:
        print(j)
        W.append(j[2]*0.1 + 0.2)  # 这是为了使画图的时候权重更加清晰
    # 画图
    G = nx.Graph()
    # 导入所有边
    G.add_weighted_edges_from(my_NET.E, weight="t")
    pos = nx.kamada_kawai_layout(G)
    nx.draw(G, pos, width=2, with_labels=True,
            edge_color=plt.cm.Greys(W),
            node_color=plt.cm.Blues(np.linspace(0.2, 0.4, len(my_NET.V))),
            node_size=300)
    plt.show()
    plt.savefig('./ReachabilityDiagram.png')
